﻿#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
//int main()
//{
//	char arr[] = "abcdef";
//	printf("%d\n", sizeof(arr));// arr是数组名 单独放在sizeof内部 计算的是数组总大小 7个字节  a b c d e f \0
//	//sizeof在乎内存空间的长度 
//	printf("%d\n", sizeof(arr + 0));//arr表示数组首元素的地址 arr+0还是首元素的地址 是地址就是4/8个字节
//	printf("%d\n", sizeof(*arr));//arr表示数组首元素地址 *arr就是首元素 大小是1个字节
//	printf("%d\n", sizeof(arr[1]));//arr[1]是第二个元素 大小是一个字节
//	printf("%d\n", sizeof(&arr));//&arr是数组地址 是地址就是4/8个字节
//	printf("%d\n", sizeof(&arr + 1));//&arr是数组的地址 +1跳过整个数组 还是地址 是地址就是4/8个字节
//	printf("%d\n", sizeof(&arr[0] + 1));//&arr[0] + 1是第二个元素的地址 大小是4/8个字节
//	return 0;
//}

#include<string.h>
//int main()
//{
//	char arr[] = "abcdef";
//	printf("%d\n", strlen(arr));//6
//	printf("%d\n", strlen(arr + 0));//arr是首元素的地址 arr+0还是首元素的地址 向后在\0之前右6个字符
//	//printf("%d\n", strlen(*arr));// 'a' --97 传过去就会出错
//	//printf("%d\n", strlen(arr[1]));// 'b' -- 98 传过去也会出错
//	printf("%d\n", strlen(&arr));//&arr是数组的地址 但它的值也是和数组首元素的地址是一样的 从第一个元素向后找的 6
//	printf("%d\n", strlen(&arr + 1));//随机值
//	printf("%d\n", strlen(&arr[0] + 1));//5
//	return 0;
//}

//int main()
//{
//	const char* p = "abcdef";//前面加上一个const会更加严谨 这个字符串不会被修改
//	printf("%d\n", sizeof(p));//随机值  p是指针变量 存放的是地址 我们计算的是指针变量的大小 4/8个字节
//	printf("%d\n", sizeof(p + 1));// p+1存的是b的地址 指向了字符b 是地址大小就是4/8个字节
//	printf("%d\n", sizeof(*p));//p的类型是char* *p就是char类型了 1个字节
//	printf("%d\n", sizeof(p[0]));// 理解思路1.p[0] == *(p+0)==*p 得到的就是字符a 1个字节  
//	//理解思路2 把常量字符串想象成数组 p可以理解为数组名 p[0]就是首元素
//	
//	printf("%d\n", sizeof(&p));// &p取的是p的地址 和这个常量字符串没什么关系 是地址就是4/8个字节 
//	printf("%d\n", sizeof(&p + 1));//&p+1是跳过p指针变量后的地址 是地址就是4/8个字节  二级指针的位移量是char*
//	printf("%d\n", sizeof(&p[0] + 1));// p[0]是首元素 &p[0]是首字符的地址 +1是第二个元素的地址 是地址就是4/8个字节
//	return 0;
//}

//int main()
//{
//	char* p = "abcdef";
//	printf("%d\n", strlen(p));//p存的是字符a的地址 也就是指向字符a  找到\0之前的所有字符 结果是6
//	printf("%d\n", strlen(p + 1));//p是指向a 那么p+1指向了
//	//printf("%d\n", strlen(*p));	// *p就是字符'a' 传给strlen的就是97 是错误的 程序会崩掉的
//	//printf("%d\n", strlen(p[0]));//p[0] 等价于 *(p+0) 等价于*p  //err
//	printf("%d\n", strlen(&p));//&p是指针变量p的地址 和字符串"abcdef"关系就不大了
//	//从p这个指针变量的起始位置开始向后数 p变量存放的地址是什么并不清楚 所以答案是随机值
//	printf("%d\n", strlen(&p + 1));//随机值 而且&p和&p+1之间没有关系 因为万一p变量里边有\0 所以并不好说这两个之间有关系
//	printf("%d\n", strlen(&p[0] + 1)); //&p[0]是首字符的地址 +1是第二个元素的地址  5
//	return 0;
//}

//二维数组
//int main()
//{
//	int a[3][4] = { 0 };
//	printf("%d\n", sizeof(a));//a是数组名 并且单独放在sizeof内部 计算的是数组的大小 单位是字节 48 = 3*4*(sizeof(int))
//	printf("%d\n", sizeof(a[0][0]));//4个字节 a[0][0]是第一行第一个元素
//	printf("%d\n", sizeof(a[0]));//a[0]是第一行的数组名 数组名单独放在sizeof内部 计算的是数组的总大小 16个字节
//	printf("%d\n", sizeof(a[0] + 1));//a[0]+1中的a[0]并没有单独放在sizeof内部 所以这里的数组名a[0]就是数组
//	//首元素的地址 --&a[0][0]这个地址 +1后是a[0][1] 是地址大小就是4/8个字节
//	printf("%d\n", sizeof(*(a[0] + 1)));//访问的就是第一行第二个元素 大小是4个字节
//	printf("%d\n", sizeof(a + 1));//a作为数组名并没有单独放在sizeof内部 a表示数组首元素的地址 是二维数组的首元素的地址 也就是第一行的地址
//	//所以a+1跳过一行 指向了第二行 a+1是第二行的地址 a+1是数组指针，是地址大小就是4/8个字节
//	printf("%d\n", sizeof(*(a + 1)));
//	//两种思路 1.a+1是第二行的地址 再进行解引用就是第二行，计算的是第二行的大小 --16字节
//	//2.*(a+1)从语法上等价于a[1]，a[1]是第二行的数组名,sizeof(*(a+1)) == sizeof(a[1]) 这就是把第二行数组名单独放在sizeof内部 
//	//计算的是第二行的大小 
//	printf("%d\n", sizeof(&a[0] + 1));//a[0]是第一行的数组名 &a[0]取出的就是数组第一行的地址 
//	//&a[0]+1就是第二行的地址 是地址大小就是4/8个字节
//	printf("%d\n", sizeof(*(&a[0] + 1)));//&a[0] + 1就是取的第二行的地址 再进行解引用就得到第二行 计算的就是第二行的大小 16个字节
//	printf("%d\n", sizeof(*a));//a是二维数组数组名但没有单独放在sizeof内部，在这表示的就是数组第一行的地址 *a得到的就是第一行 
//	//计算的就是第一行的大小 16字节
//	//也可以这样理解 *a == *(a+0) == a[0]
//	printf("%d\n", sizeof(a[3]));//越界虽然是越界了 因为sizeof内部不进行计算 所以并没有访问a[3] 
//	//推算a[3]是一个int[4]类型的一个数组 而sizeof推算长度的时候都是根据类型来计算长度的 sizeof不会真是访问这块空间 所以存不存在都可以
//	// a[3]无需真实存在 仅仅通过类型的推断就能算出长度 因为sizeof(数据类型)就可得到结果
//	//a[3]是第四行的数组名 单独放在sizeof内部，计算的是第四行的大小 16个字节
//	return 0;
//}

//int main()
//{
//	int a[5] = { 1, 2, 3, 4, 5 };//1 2 3 4 5
//	int* ptr = (int*)(&a + 1);//&a是取出整个数组的地址 &a+1是跳过整个数组的 
//	//&a的类型是int(*)[5] 那么&a+1也是这个类型的指针 因为ptr是整型类型的指针 所以后面要进行强制类型转换 不然会报警告的
//	// 这时候ptr和&a+1指向的是同一个空间
//	printf("%d,%d", *(a + 1), *(ptr - 1));
//	//ptr是整型指针 -1就是向前1个整型 便指向了5 解引用拿到的就是5  
//	//a是首元素的地址 +1就是指向了第二个元素 再进行解引用得到的就是2
//	return 0;
//}

////在X86（32位）环境下
////假设结构体的⼤⼩是20个字节
////程序输出的结构是啥？
////struct Test
////{
////int Num;
////char* pcName;
////short sDate;
////char cha[2];
////short sBa[4];
////}* p = (struct Test*)0x100000;  //结构体加个*就是结构体指针 这时候并创建了指针变量p
////0x100000是个十六进制整数 编译器认为是个int类型的整数  把它赋给指针变量p 需要进行强制类型转换 变成一个结构体指针
//// 
////指针加减整数
////int main()
////{
////	printf("%p\n", p + 0x1);//0x1 == 1  结构体指针+1 是要跳过一个结构体的 在这的话+1的效果就是+20个字节
////	 0x100000 + 20 -> 0x100014 在这的话20可以转换为16进制为14 最终打印地址 00100014
////	printf("%p\n", (unsigned long)p + 0x1);//p被强制类型转换为unsigned long就不是指针了 变为整型了 整型值+1就是+1，变为0x100001
////	打印00100001
////	printf("%p\n", (unsigned int*)p + 0x1);//p被强转为无符号整形指针 +1就是跳过4个字节 变为0x100004  打印00100004
////	return 0;
////}

int main()
{
	//在这的话 初始化一定要识别正确
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//这里边的小括号代表了就是逗号表达式 得到的结果就是1 3 5 
	//后边没元素了 接着就赋值0 0 0 所以数组真正存储的就是1 3 5 0 0 0
	int* p;
	p = a[0];//a[0]是第一行的数组名 在这表示首元素的地址--a[0][0]的地址
	printf("%d", p[0]);//p[0] == *(p+0) == *p 那么结果是1
	return 0;
}